﻿using HybridCLR;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;
using ZyGame.Execute;
using ZyGame.Resource;
using ZyGame.UI;

namespace ZyGame.Game
{
    [UIOptions("Resources/Loading.prefab", UIOptions.UIDepth.Middle)]
    public sealed class LoadingWindow : UIWindow
    {
        private Slider slider;

        public override void Enable()
        {
            base.Enable();
            this.GetChild("ver").SetText($"ver {Application.version}");
        }

        public void UpdateSliderValue(float value)
        {
            if (slider == null)
            {
                slider = GetChild("Slider")?.GetComponent<Slider>();
            }

            if (slider == null)
            {
                return;
            }

            slider.value = value;
        }
    }

    public class Startup : MonoBehaviour
    {
        public VideoPlayer player;
        private bool isJoint = false;

        void Start()
        {
            GameObject.DontDestroyOnLoad(this.gameObject);
            Screen.sleepTimeout = -1;
            Client.Game.Initialized();
            if (player != null)
            {
                return;
            }

            Joint();
        }

        private void Update()
        {
            if (player == null)
            {
                return;
            }

            if (Input.GetMouseButtonDown(0) || Input.touchCount > 0 || player.frame == (long)player.frameCount - 1)
            {
                Joint();
            }
        }

        private void Joint()
        {
            if (isJoint)
            {
                return;
            }

            if (player != null)
            {
                player.Stop();
                GameObject.DestroyImmediate(player.gameObject);
                player = null;
            }

            isJoint = true;
            Client.Window.GetOrOpen<LoadingWindow>()
                .Deactivated("Slider")
                .Activated("text")
                .SetText("text", "Check Resource Update...");
            Client.Resource.CheckUpdateAssetList(AppConfig.instance.gameOptions.mainPackageName)
                .Execute(CheckUpdateCompleted);
        }

        private void CheckUpdateCompleted(Runnable<List<PackageInfo>> args)
        {
            if (args == null || !args.EnsureExecutedSuccessful())
            {
                Client.Window.MsgBox("Failed to check for resource updates! Please check your network and try again！", Application.Quit);
                return;
            }

            CheckUpdateRunnable checkUpdateRunnable = (CheckUpdateRunnable)args;
            if (!checkUpdateRunnable.EnsureNeedUpdateAssets())
            {
                UpdateAssetCompleted();
                return;
            }

            List<PackageInfo> needUpdateList = checkUpdateRunnable.result;
            Client.Window.MsgBox($"Detected {checkUpdateRunnable.GetUpdateLength()}M resource updates. Do you want to update now?", OnUpdateAsset, UpdateAssetCompleted);

            void OnUpdateAsset()
            {
                LoadingWindow view = Client.Window.GetOrOpen<LoadingWindow>();
                view.Activated("Slider").Activated("text").SetText("text", "Updating resources...");
                Client.Resource.UpdateModuleAssetList(needUpdateList)
                    .ListenerProgres(view.UpdateSliderValue)
                    .Execute(UpdateAssetCompleted);
            }
        }

        void UpdateAssetCompleted()
        {
            LoadingBundleRunnable preload = Client.Resource.PreLoadBundle(AppConfig.instance.gameOptions.mainPackageName);

            LoadingWindow view = Client.Window.GetOrOpen<LoadingWindow>();
            view.SetText("text", "Loading resources...")
                .Activated("Slider");

            preload.SetProgressCallback(view.UpdateSliderValue)
                .Execute(OnEntryGame);
        }

        private void OnEntryGame()
        {
            Client.Console.WriteLine("Open Game");
            if (AppConfig.HasUseScriptsHotfix() is false)
            {
                Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().Where(x => { return x.GetName().Name == AppConfig.instance.hotfixOptions.dllName.Split('.')[0]; }).FirstOrDefault();

                assembly.InvokeMethod("Main", new object[] { });
                return;
            }

            Client.Tools.StartCoroutine(LoadAssembly(AppConfig.instance.hotfixOptions.dllName, AppConfig.instance.hotfixOptions.aotList.ToArray()));
        }

        public IEnumerator LoadAssembly(string assemblyName, params string[] aotList)
        {
            if (aotList is not null && aotList.Length > 0)
            {
                HomologousImageMode mode = HomologousImageMode.SuperSet;
                foreach (var item in aotList)
                {
                    Runnable<TextAsset> runnable = Client.Resource.LoadAssetAsync<TextAsset>(item + ".bytes");
                    yield return runnable.Waitting();
                    if (runnable.EnsureExecutedSuccessful())
                    {
                        LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(runnable.result.bytes, mode);
                        Debug.Log($"LoadMetadataForAOTAssembly:{item}. mode:{mode} ret:{err}");
                    }
                }
            }

            Runnable<TextAsset> runnable1 = Client.Resource.LoadAssetAsync<TextAsset>(assemblyName);
            yield return runnable1.Waitting();
            if (!runnable1.EnsureExecutedSuccessful())
            {
                yield break;
            }

            try
            {
                Assembly.Load(runnable1.result.bytes).InvokeMethod("Main", new object[] { });
            }
            catch (Exception e)
            {
                Client.Console.WriteError(e);
                Client.Window.MsgBox("Loading Game Fail", () => { Application.Quit(); });
            }
        }
    }
}